
# https://blog.csdn.net/dnty00/article/details/126201648 mmdetection目标检测模型最强优化
# SwinTransformer作为Backbone，
# FPN作为Neck，多尺度检测
# TOOD模型作为Bbox_head，并加入一系列的模型调优策略，得到目前单GPU最优精度模型。

# TOOD: Task-aligned One-stage Object Detection
# 针对目前模型的分类与定位任务交互少，导致预测不一致的问题，
# TOOD设计一个新的 head 结构来更好的将分类和定位任务对齐。

# 首先，任务对齐head在FPN 输出特征中进行分类和定位；
# 然后，采用任务对齐学习（TAL）用对齐度量参数来计算两者的对齐信号（alignment signal）；
# 最后，在反向传播中使用对齐信号动态调整分类的得分和定位的位置，
# 从而在预测过程中对两个任务进行对齐，为任务交互以及任务相关特征提供更好的平衡，
# 提升模型的精度。

# 在分类头部用QualityFocalLoss代替FocalLoss作为分类损失，
# 兼顾分类得分和质量评估得分，这样可以保证训练和测试的一致性，从而解决模型类别不均衡问题。

# 在回归头部用GIoULoss作为回归损失
# 设计了TaskAlignedAssigner进行正负样本分配

checkpoint_config = dict(interval=1)
log_config = dict(interval=50, hooks=[dict(type='TextLoggerHook')])
custom_hooks = [dict(type='NumClassCheckHook')]
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)]
opencv_num_threads = 0
mp_start_method = 'fork'

set_img_scale=(512, 512)


# pretrained = 'https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_small_patch4_window7_224.pth'
pretrained = 'checkpoints/swin_tiny_patch4_window7_224.pth'

model = dict(
    type='TOOD',
    backbone=dict( # Backbone指的是模型的主干网络，主要用来特征提取；
        type='SwinTransformer',
        embed_dims=96,
        depths=[2, 2, 18, 2],
        num_heads=[3, 6, 12, 24],
        window_size=7,
        mlp_ratio=4,
        qkv_bias=True,
        qk_scale=None,
        drop_rate=0.0,
        attn_drop_rate=0.0,
        drop_path_rate=0.2,
        patch_norm=True,
        out_indices=(1, 2, 3),
        with_cp=False,
        convert_weights=True,
        init_cfg=dict(type='Pretrained', checkpoint=pretrained)),

    neck=[ #Neck主要用来做特征融合，以FPN（特征金字塔）为代表；
        dict(
            type='FPN',
            in_channels=[192, 384, 768],
            out_channels=256,
            start_level=0,
            add_extra_convs='on_output',
            num_outs=5)
    ],
    bbox_head=dict(
        #Bbox_head指的是目标检测或者目标分割等任务的头部，分为分类和回归两个子任务。
        # 将分类和定位任务对齐。
        type='TOODHead',
        num_classes=20,
        in_channels=256,
        stacked_convs=6,
        feat_channels=256,
        anchor_type='anchor_free',
        anchor_generator=dict(
            type='AnchorGenerator',
            ratios=[1.0],
            octave_base_scale=8,
            scales_per_octave=1,
            strides=[8, 16, 32, 64, 128]),
        bbox_coder=dict(
            type='DeltaXYWHBBoxCoder',
            target_means=[0.0, 0.0, 0.0, 0.0],
            target_stds=[0.1, 0.1, 0.2, 0.2]),
        initial_loss_cls=dict(
            type='FocalLoss',
            use_sigmoid=True,
            activated=True,
            gamma=2.0,
            alpha=0.25,
            loss_weight=1.0),
        loss_cls=dict(
            #分类头部用QualityFocalLoss代替FocalLoss作为分类损失，
            # 兼顾分类得分和质量评估得分，这样可以保证训练和测试的一致性，
            # 从而解决模型类别不均衡问题。
            type='QualityFocalLoss',
            use_sigmoid=True,
            activated=True,
            beta=2.0,
            loss_weight=1.0),

        # 在回归头部用GIoULoss作为回归损失
        loss_bbox=dict(type='GIoULoss', loss_weight=2.0),
        num_dcn=2),
        
    train_cfg=dict(
        initial_epoch=4,
        initial_assigner=dict(type='ATSSAssigner', topk=9),

        #设计了TaskAlignedAssigner进行正负样本分配
        assigner=dict(type='TaskAlignedAssigner', topk=13),
        alpha=1,
        beta=6,
        allowed_border=-1,
        pos_weight=-1,
        debug=False),
    test_cfg=dict(
        nms_pre=1000,
        min_bbox_size=0,
        score_thr=0.05,
        nms=dict(type='nms', iou_threshold=0.6),
        max_per_img=100))
dataset_type = 'CocoDataset'
data_root = 'data/coco/'
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(
        type='Resize',
        img_scale=set_img_scale,#[(1333, 640), (1333, 800)],
        multiscale_mode='range',
        keep_ratio=True,
        backend='pillow'),
    dict(type='RandomFlip', flip_ratio=0.0),
    dict(
        type='Normalize',
        mean=[123.675, 116.28, 103.53],
        std=[58.395, 57.12, 57.375],
        to_rgb=True),
    dict(type='Pad', size_divisor=128),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=set_img_scale,#(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True, backend='pillow'),
            dict(type='RandomFlip'),
            dict(
                type='Normalize',
                mean=[123.675, 116.28, 103.53],
                std=[58.395, 57.12, 57.375],
                to_rgb=True),
            dict(type='Pad', size_divisor=128),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img'])
        ])
]
data = dict( #img_scale=set_img_scale,  # [(1333, 640), (1333, 800)],
    samples_per_gpu=4,
    workers_per_gpu=4,    
    train=dict(
        type='RepeatDataset',
        times=5,#单次 训练重复5次
        dataset=dict(
            type='CocoDataset',
            ann_file='data/coco/annotations/instances_train2017.json',
            img_prefix='data/coco/train2017/',
            pipeline=[
                dict(type='LoadImageFromFile'),
                dict(type='LoadAnnotations', with_bbox=True),
                dict(
                    type='Resize',
                    img_scale=set_img_scale,  # [(1333, 640), (1333, 800)],
                    multiscale_mode='range',
                    keep_ratio=True,
                    backend='pillow'),
                dict(type='RandomFlip', flip_ratio=0.0),
                dict(
                    type='Normalize',
                    mean=[123.675, 116.28, 103.53],
                    std=[58.395, 57.12, 57.375],
                    to_rgb=True),
                dict(type='Pad', size_divisor=128),
                dict(type='DefaultFormatBundle'),
                dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
            ])),

    val=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_val2017.json',
        img_prefix='data/coco/val2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=set_img_scale,#(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True, backend='pillow'),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=128),
                    dict(type='ImageToTensor', keys=['img']),
                    dict(type='Collect', keys=['img'])
                ])
        ]),
    test=dict(
        type='CocoDataset',
        ann_file='data/coco/annotations/instances_val2017.json',
        img_prefix='data/coco/val2017/',
        pipeline=[
            dict(type='LoadImageFromFile'),
            dict(
                type='MultiScaleFlipAug',
                img_scale=set_img_scale,#(1333, 800),
                flip=False,
                transforms=[
                    dict(type='Resize', keep_ratio=True, backend='pillow'),
                    dict(type='RandomFlip'),
                    dict(
                        type='Normalize',
                        mean=[123.675, 116.28, 103.53],
                        std=[58.395, 57.12, 57.375],
                        to_rgb=True),
                    dict(type='Pad', size_divisor=128),
                    dict(type='ImageToTensor', keys=['img']),
                    dict(type='Collect', keys=['img'])
                ])
        ]))
evaluation = dict(interval=1, metric='bbox')

#optimizer_config = dict(grad_clip=None)
optimizer_config=dict (grad_clip=dict (max_norm=50,norm_type=2))

optimizer = dict(
    type='AdamW',
    lr= 1e-5, 
    betas=(0.9, 0.999),
    weight_decay=0.1,
    paramwise_cfg=dict(
        custom_keys=dict(
            absolute_pos_embed=dict(decay_mult=0.0),
            relative_position_bias_table=dict(decay_mult=0.0),
            norm=dict(decay_mult=0.0))))
# lr_config = dict(
#     policy='step',
#     warmup='linear',
#     warmup_iters=500,
#     warmup_ratio=0.001,
#     step=[8, 11])


#  使用动量调度加速模型收敛
# 支持动量调度器根据学习率修改模型的动量，这可以使模型以更快的方式收敛。
# Momentum 调度器通常与 LR 调度器一起使用
# https://developer.aliyun.com/article/1078406
lr_config = dict(
    policy='cyclic',
    target_ratio=(10, 1e-5),
    cyclic_times=1,
    step_ratio_up=0.4,
)


momentum_config = dict(
    policy='cyclic',
    target_ratio=(0.85 / 0.95, 1),
    cyclic_times=1,
    step_ratio_up=0.4,
)
runner = dict(type='EpochBasedRunner', max_epochs=12)
work_dir = './work_dirs/tood_swin-s-p4-w12_fpn_mstrain_3x_coco'
auto_resume = False
gpu_ids = [0]
